#version 410

const int MAX_POINT_LIGHTS = 10;
const int MAX_SPOT_LIGHTS = 10;

in vec2 texCoord;
in vec3 normal;
in vec3 tangent;
in mat4 transformOut;
in vec3 worldPos;
flat in uint referenceIndex;

out vec4 FragColor;

struct Light 
{
	vec3 color;
	float ambientIntensity;
	float diffuseIntensity;
};

struct DirectionalLight 
{
	Light base;
	vec3 direction;
};

struct PointLight 
{
	Light base;
	vec3 position;
	float attConstant;
	float attLinear;
	float attExp;
	mat4 projLightTrans;
};

struct SpotLight 
{
	PointLight base;
	vec3 direction;
	float cutoff;
};

uniform int numPointLights;
uniform int numSpotLights;
uniform DirectionalLight directionalLight;
uniform PointLight pointLights[MAX_POINT_LIGHTS];
uniform SpotLight spotLights[MAX_SPOT_LIGHTS];
uniform sampler2D diffuseMap;
uniform sampler2D specularMap;
uniform sampler2D ambientMap;
uniform sampler2D emissiveMap;
uniform sampler2D heightMap;
uniform sampler2D normalMap;
uniform sampler2D shininessMap;
uniform sampler2D opacityMap;
uniform sampler2D displacementMap;
uniform sampler2D lightMap;
uniform sampler2D reflectionMap;
uniform sampler2D shadowMap;
uniform vec3 eyeWorldPos;
uniform float specularIntensity;
uniform float specularPower;
uniform uint currentObjIndex;
uniform uint currentRefIndex;
uniform uint objectIndex;

float computeLinearFogFactor()
{
	float factor;
	factor = (190 - 100 ) / (200 - 190 );
	factor = clamp( factor, 0.0, 1.0 );
	return factor;
}

float CalcShadowFactor(vec4 LightSpacePos)
{
    vec3 ProjCoords = LightSpacePos.xyz / LightSpacePos.w;
    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjCoords.y + 0.5;
    float z = 0.5 * ProjCoords.z + 0.5;
    float Depth = texture(shadowMap, UVCoords).x;

    if (Depth < z + .0001)
        return 0.0;
    else
        return 1.0;
}

vec4 CalcLightInternal(Light light, vec3 lightDirection, vec3 norm, float shadowFactor)
{
	vec4 ambientColor = vec4(light.color, 1.0f) * light.ambientIntensity;
	float diffuseFactor = dot(norm, -lightDirection);

	vec4 diffuseColor  = vec4(0, 0, 0, 0);
	vec4 specularColor = vec4(0, 0, 0, 0);

	if (diffuseFactor > 0) 
	{
		diffuseColor = vec4(light.color, 1.0f) * light.diffuseIntensity * diffuseFactor;
        vec3 vertexToEye = normalize(eyeWorldPos - worldPos);
        vec3 lightReflect = normalize(reflect(lightDirection, norm));
        float specularFactor = dot(vertexToEye, lightReflect);
		specularFactor = pow(specularFactor, specularPower);
		if (specularFactor > 0)
		{
			specularColor = vec4(light.color, 1.0f) * specularIntensity * specularFactor;
		}
	}

	return (ambientColor + shadowFactor * diffuseColor + specularColor);
}

vec4 CalcDirectionalLight(vec3 norm)
{
	return CalcLightInternal(directionalLight.base, directionalLight.direction, norm, 1.0);
}

vec4 CalcPointLight(PointLight l, vec3 norm, vec4 LightSpacePos)
{
	vec3 lightDirection = worldPos - l.position;
	float distance = length(lightDirection);
	lightDirection = normalize(lightDirection);
	float shadowFactor = CalcShadowFactor(LightSpacePos);

	vec4 color = CalcLightInternal(l.base, lightDirection, norm, shadowFactor);
	float attenuation =  l.attConstant + l.attLinear * distance + l.attExp * distance * distance;

	return color / attenuation;
}

vec4 CalcSpotLight(SpotLight l, vec3 norm, vec4 LightSpacePos)
{
	vec3 lightToPixel = normalize(worldPos - l.base.position);
	float spotFactor = dot(lightToPixel, l.direction);
	if (spotFactor > l.cutoff)
	{
		vec4 color = CalcPointLight(l.base, norm, LightSpacePos);
		return color * (1.0 - (1.0 - spotFactor) * 1.0/(1.0 - l.cutoff));
	}
	else
	{
		return vec4(0,0,0,0);
	}
}

vec3 CalcBumpedNormal()
{
    vec3 norm = normalize(normal);
    vec3 tang = normalize(tangent);
    tang = normalize(tang - dot(tang, norm) * norm);
    vec3 biTangent = cross(tang, norm);
    vec3 bmNormal = texture(normalMap, texCoord).xyz;
    bmNormal = 2.0 * bmNormal - vec3(1.0, 1.0, 1.0);
    vec3 nNormal;
    mat3 transBumpMap = mat3(tang, biTangent, norm);
    nNormal = transBumpMap * bmNormal;
    nNormal = normalize(nNormal);
    return nNormal;
}

void main()
{
	vec4 alpha;
	alpha = texture2D(opacityMap, texCoord.xy);
	if(alpha.w == 0.0)// || length(worldPos - eyeWorldPos) > 200)
		discard;

	vec3 norm = CalcBumpedNormal();
	vec4 totalLight = CalcDirectionalLight(norm);

	for (int i = 0 ; i < numPointLights ; i++)
	{
		vec4 LightSpacePos = pointLights[i].projLightTrans * vec4(worldPos,1.0);
		totalLight += CalcPointLight(pointLights[i], norm, LightSpacePos);
	}

	for (int i = 0 ; i < numSpotLights ; i++)
	{
		vec4 LightSpacePos = spotLights[i].base.projLightTrans * vec4(worldPos,1.0);
        totalLight += CalcSpotLight(spotLights[i], norm, LightSpacePos);
	}

	vec4 col = texture2D(diffuseMap, texCoord.xy) * totalLight;
	
	float fogFactor = computeLinearFogFactor();
	vec4 fogColor = fogFactor * vec4(0.7,0.7,0.7,1.0);
	FragColor = vec4(col.x,col.y,col.z,alpha.w) * fogFactor + fogColor * (1.0 - fogFactor);
}